package com.fsserver.config.interceptor;

import com.fsserver.common.utils.MDCTraceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 链路追踪拦截器
 * 用于处理请求的traceId，确保每个请求都有唯一的traceId
 * 同时记录请求的执行时间和关键信息
 * 
 * @author fsserver
 * @date 2025-03-13
 */
public class TraceInterceptor implements HandlerInterceptor {
    private static final Logger log = LoggerFactory.getLogger(TraceInterceptor.class);

    /**
     * 请求头中的traceId名称
     */
    private static final String TRACE_ID_HEADER = "X-Trace-Id";

    /**
     * 请求头中的业务ID名称
     */
    private static final String BUSINESS_ID_HEADER = "X-Business-Id";
    
    /**
     * 请求头中的用户ID名称
     */
    private static final String USER_ID_HEADER = "X-User-Id";
    
    /**
     * 慢请求阈值（毫秒）
     */
    private static final long SLOW_REQUEST_THRESHOLD = 1000;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 记录请求开始时间
        long startTime = System.currentTimeMillis();
        MDCTraceUtils.setRequestStartTime(startTime);
        
        // 记录请求路径
        String requestURI = request.getRequestURI();
        MDCTraceUtils.setRequestPath(requestURI);
        
        // 尝试从请求头中获取traceId
        String traceId = request.getHeader(TRACE_ID_HEADER);
        
        // 如果请求头中没有traceId，则生成一个新的
        if (!StringUtils.hasText(traceId)) {
            traceId = MDCTraceUtils.generateTraceId();
        }
        
        // 设置traceId到MDC中
        MDCTraceUtils.setTraceId(traceId);
        
        // 尝试从请求头中获取业务ID
        String businessId = request.getHeader(BUSINESS_ID_HEADER);
        if (StringUtils.hasText(businessId)) {
            MDCTraceUtils.setBusinessId(businessId);
        }
        
        // 尝试从请求头中获取用户ID
        String userId = request.getHeader(USER_ID_HEADER);
        if (StringUtils.hasText(userId)) {
            MDCTraceUtils.setUserId(userId);
        }
        
        // 将traceId添加到响应头中，便于客户端跟踪
        response.setHeader(TRACE_ID_HEADER, traceId);
        
        // 记录请求开始日志，但跳过Swagger相关资源
        if (log.isDebugEnabled() && !isSwaggerResource(requestURI)) {
            String method = request.getMethod();
            String handlerName = getHandlerName(handler);
            
            log.debug("请求开始: {} {} (Handler: {})", method, requestURI, handlerName);
        }
        
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        // 不做任何处理
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        try {
            // 计算请求处理时间
            long endTime = System.currentTimeMillis();
            long startTime = MDCTraceUtils.getRequestStartTime();
            long duration = (startTime > 0) ? (endTime - startTime) : -1;
            
            // 获取请求信息
            String method = request.getMethod();
            String requestURI = MDCTraceUtils.getRequestPath();
            int status = response.getStatus();
            String handlerName = getHandlerName(handler);
            
            // 跳过Swagger相关资源的日志记录
            if (isSwaggerResource(requestURI)) {
                return;
            }
            
            // 记录请求结束日志
            if (duration > SLOW_REQUEST_THRESHOLD) {
                // 对于慢请求，记录更详细的信息
                Map<String, String> requestHeaders = getRequestHeaders(request);
                String queryString = request.getQueryString();
                String fullPath = StringUtils.hasText(queryString) ? requestURI + "?" + queryString : requestURI;
                
                log.warn("慢请求: {} {} 处理耗时: {}ms, 状态码: {}, Handler: {}, 请求头: {}", 
                        method, fullPath, duration, status, handlerName, requestHeaders);
            } else if (ex != null) {
                // 对于异常请求，记录异常信息
                log.error("请求异常: {} {} 处理耗时: {}ms, 状态码: {}, Handler: {}, 异常: {}", 
                        method, requestURI, duration, status, handlerName, ex.getMessage());
            } else {
                // 对于正常请求，记录基本信息
                log.info("完成请求: {} {} 处理耗时: {}ms, 状态码: {}", 
                        method, requestURI, duration, status);
            }
        } finally {
            // 请求完成后清除MDC中的数据，防止内存泄漏
            MDCTraceUtils.clear();
        }
    }
    
    /**
     * 判断是否为Swagger相关资源
     * 
     * @param uri 请求URI
     * @return 是否为Swagger相关资源
     */
    private boolean isSwaggerResource(String uri) {
        return uri != null && (
            uri.contains("/webjars/") || 
            uri.contains("/swagger-resources") || 
            uri.contains("/v2/api-docs") || 
            uri.contains("/doc.html")
        );
    }
    
    /**
     * 获取处理器名称
     * 
     * @param handler 处理器对象
     * @return 处理器名称
     */
    private String getHandlerName(Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            return handlerMethod.getBeanType().getSimpleName() + "." + handlerMethod.getMethod().getName();
        }
        return handler != null ? handler.toString() : "unknown";
    }
    
    /**
     * 获取请求头信息
     * 
     * @param request HTTP请求
     * @return 请求头信息
     */
    private Map<String, String> getRequestHeaders(HttpServletRequest request) {
        Map<String, String> headers = new HashMap<>(16);
        Enumeration<String> headerNames = request.getHeaderNames();
        
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            // 排除一些敏感或不必要的头信息
            if (!"cookie".equalsIgnoreCase(headerName) && 
                !"authorization".equalsIgnoreCase(headerName)) {
                headers.put(headerName, request.getHeader(headerName));
            }
        }
        
        return headers;
    }
} 